package controller

import (
	"aqchain/pkg/entity"
	"aqchain/pkg/log"
	"aqchain/pkg/model"
	"aqchain/pkg/utils"
	"fmt"
	"gitee.com/aqchain/go-ethereum/crypto"
	"github.com/fogleman/gg"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/signintech/gopdf"
	"go.uber.org/zap"
	"io"
	"strconv"

	"net/http"
	"os"
	"path"
	"path/filepath"
	"time"
)

// GetFile 获取上传的文件
func (c *Controller) GetFile(ctx *gin.Context) {
	var params entity.GetFileUri
	err := ctx.BindUri(&params)
	if err != nil {
		log.Logger.Debug("error", zap.Error(err), zap.String("code", errParams.Error()))
		ctx.JSON(http.StatusBadRequest, ErrorResponse(errParams))
		return
	}
	ctx.File(filepath.Join(c.config.Local.FileDir, params.DCI))
}

// GetCertificatePDF GetCertificate 获取证书文件 todo 控制权限
func (c *Controller) GetCertificatePDF(ctx *gin.Context) {
	// 绑定参数
	var param entity.GetCertificateUri
	err := ctx.BindUri(&param)
	if err != nil {
		log.Logger.Debug("error", zap.Error(err), zap.String("code", errParams.Error()))
		ctx.JSON(http.StatusBadRequest, ErrorResponse(errParams))
		return
	}
	// 获取作品数据
	var itemRecord model.Item
	err = c.db.Preload("Creator").Preload("Owner").Preload("Create").Preload("Transfer").Where(model.Item{DCI: param.DCI}).First(&itemRecord).Error
	if err != nil {
		log.Logger.Debug("error", zap.Error(err), zap.String("code", errItemNotExist.Error()))
		ctx.JSON(http.StatusBadRequest, ErrorResponse(errItemNotExist))
		return
	}
	var itemEventRecord model.ItemEvent
	err = c.db.Where(model.ItemEvent{DCI: param.DCI, EventName: "create"}).First(&itemEventRecord).Error
	if err != nil {
		ctx.JSON(http.StatusBadRequest, ErrorResponse(errItemNotExist))
		return
	}

	// 转换
	data := entity.Item{
		DCI:             itemRecord.DCI,
		Name:            itemRecord.Name,
		CreatorAddress:  itemRecord.CreatorAddress,
		CreatorUsername: itemRecord.Creator.Username,
		OwnerAddress:    itemRecord.OwnerAddress,
		OwnerUsername:   itemRecord.Owner.Username,
		ApprovalAddress: itemRecord.ApprovalAddress,
		TxHash:          itemEventRecord.TxHash,
		BlockNumber:     itemEventRecord.BlockNumber,
		BlockTime:       itemEventRecord.BlockTime,
	}

	// 判断证书是否已经生成
	filePath := fmt.Sprintf("%s.pdf", filepath.Join(c.config.Local.CertificateDir, data.DCI))
	_, err = os.Stat(filePath)
	if err != nil {
		err = c.generateCertificatePDF(data)
		if err != nil {
			log.Logger.Debug("error", zap.Error(err), zap.String("code", errGenerateCertificate.Error()))
			ctx.JSON(http.StatusInternalServerError, ErrorResponse(errGenerateCertificate))
			return
		}
	}
	ctx.File(filePath)
}

// Upload 上传文件生成数字指纹
func (c *Controller) Upload(ctx *gin.Context) {
	// 绑定参数
	var params entity.UploadItemForm
	err := binding.FormMultipart.Bind(ctx.Request, &params)
	if err != nil {
		log.Logger.Debug("error", zap.Error(err), zap.String("code", errParams.Error()))
		ctx.JSON(http.StatusBadRequest, ErrorResponse(errParams))
		return
	}
	// 检查签名 todo 修改为检查结构体签名来验证内容
	err = utils.VerifySignature(params.Address, "Item Operation Sign Message", params.Signature)
	if err != nil {
		log.Logger.Debug("error", zap.Error(err), zap.String("code", errSignature.Error()))
		ctx.JSON(http.StatusBadRequest, ErrorResponse(errSignature))
		return
	}
	// 生成数字指纹
	src, err := params.File.Open()
	if err != nil {
		log.Logger.Debug("error", zap.Error(err), zap.String("code", err.Error()))
		ctx.JSON(http.StatusInternalServerError, ErrorResponse(err))
		return
	}
	fileBytes, err := io.ReadAll(src)
	// todo 获取文件的类型
	params.Category = utils.GetFileType(fileBytes[:10])
	if err != nil {
		log.Logger.Debug("error", zap.Error(err), zap.String("code", err.Error()))
		ctx.JSON(http.StatusInternalServerError, ErrorResponse(err))
		return
	}
	dci := crypto.Keccak256Hash(fileBytes).String()
	// 保存文件
	if params.IsSave {
		err = ctx.SaveUploadedFile(params.File, path.Join(c.config.Local.FileDir, dci))
		if err != nil {
			log.Logger.Debug("error", zap.Error(err), zap.String("code", err.Error()))
			ctx.JSON(http.StatusInternalServerError, ErrorResponse(err))
			return
		}
	}
	// 数据库操作
	data := model.Item{
		DCI:         dci,
		Name:        params.Name,
		Description: params.Description,
		Category:    params.Category,
		FileName:    params.FileName,
		FileSize:    params.FileSize,
		FileType:    params.FileType,
	}
	err = c.db.Select("DCI", "Name", "Description", "Category", "FileName", "FileSize", "FileType").Create(&data).Error
	if err != nil {
		log.Logger.Debug("error", zap.Error(err), zap.String("code", errDBError.Error()))
		ctx.JSON(http.StatusInternalServerError, ErrorResponse(errDBError))
		return
	}
	// 返回结果
	result := entity.Item{
		DCI:         data.DCI,
		Name:        data.Name,
		Description: data.Description,
		Category:    data.Category,
	}
	ctx.JSON(http.StatusOK, SuccessResponse(result))
}

func (c *Controller) GetItem(ctx *gin.Context) {
	// 绑定参数
	var param entity.GetItemUri
	err := ctx.BindUri(&param)
	if err != nil {
		log.Logger.Debug("error", zap.Error(err), zap.String("code", errParams.Error()))
		ctx.JSON(http.StatusBadRequest, ErrorResponse(errParams))
		return
	}
	// 获取Item
	var itemRecord model.Item
	err = c.db.Preload("Creator").Preload("Owner").Preload("Create").Preload("Transfer").
		Where(model.Item{DCI: param.DCI}).
		First(&itemRecord).Error
	if err != nil {
		log.Logger.Debug("error", zap.Error(err), zap.String("code", errDBError.Error()))
		ctx.JSON(http.StatusInternalServerError, ErrorResponse(errDBError))
		return
	}

	// 转换并返回
	data := entity.Item{
		DCI:             itemRecord.DCI,
		Name:            itemRecord.Name,
		Description:     itemRecord.Description,
		Category:        itemRecord.Category,
		CreatorAddress:  itemRecord.CreatorAddress,
		CreatorUsername: itemRecord.Creator.Username,
		OwnerAddress:    itemRecord.OwnerAddress,
		OwnerUsername:   itemRecord.Owner.Username,
		ContractAddress: itemRecord.ContractAddress,
		ApprovalAddress: itemRecord.ApprovalAddress,
		TxHash:          itemRecord.Transfer.TxHash,
		BlockNumber:     itemRecord.Transfer.BlockNumber,
		BlockTime:       itemRecord.Transfer.BlockTime,
		BlockTimeString: time.Unix(int64(itemRecord.Transfer.BlockTime), 0).Format("2006-01-02 15:04:05"),
	}
	ctx.JSON(http.StatusOK, SuccessResponse(data))
}

func (c *Controller) GetItems(ctx *gin.Context) {
	// 绑定参数
	var params entity.GetItemsQuery
	err := ctx.BindQuery(&params)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, ErrorResponse(err))
		return
	}

	// 查询
	var records []entity.Item

	db := c.db.Select("items.dci,items.name,items.description,items.category,creator.username as creatorUsername,owner.username as ownerUsername,items.approval_address,items.file_name,items.file_size,items.file_type,items.token_uri,items.token_id,items.creator_address,items.owner_address,items.contract_address,items.approval_address,items.create_tx_hash,items.transfer_tx_hash,t.block_time").
		Joins(" JOIN item_events as t ON items.transfer_tx_hash = t.tx_hash").
		Joins(" JOIN item_events as c ON items.create_tx_hash = c.tx_hash").
		Joins(" JOIN accounts as creator ON items.creator_address = creator.address").
		Joins(" JOIN accounts as owner ON items.owner_address = owner.address")

	//按照作品创建时间查询
	if params.IsBlockTimeSearch() {
		begin, end, err := params.Convert()
		if err != nil {
			log.Logger.Debug("error", zap.Error(err), zap.String("code", err.Error()))
			ctx.JSON(http.StatusBadRequest, ErrorResponse(err))
		}
		db = db.Where("t.block_time between ? and ?", begin, end)
	}

	//按照作品名称去查找
	if params.IsNameSearch() {
		db = db.Where("name like ?", fmt.Sprintf("%s%s%s", "%", params.Name, "%"))
	}
	// 分页
	if params.IsPagination() {
		db = db.Limit(int(params.PageSize)).Offset(int((params.Current - 1) * params.PageSize))
	}

	if params.Type == "create" {
		db = db.Where("creator_address = ?", params.Address).Where("owner_address = ?", params.Address)
	} else if params.Type == "in" {
		db = db.Where("creator_address != ?", params.Address).Where("owner_address = ?", params.Address)
	} else if params.Type == "out" {
		db = db.Where("creator_address = ?", params.Address).Where("owner_address != ?", params.Address)
	}
	db = db.Order("token_id desc")
	err = db.Find(&records).Error
	if err != nil {
		log.Logger.Debug("error", zap.String("error", err.Error()), zap.String("code", errDBError.Error()))
		ctx.JSON(http.StatusInternalServerError, ErrorResponse(errDBError))
		return
	}

	ctx.JSON(http.StatusOK, SuccessResponse(records))
}

func (c *Controller) GetEventItems(ctx *gin.Context) {
	// 绑定参数
	var params entity.GetItemEventsQuery
	err := ctx.BindQuery(&params)
	if err != nil {
		log.Logger.Debug("error", zap.Error(err), zap.String("code", errParams.Error()))
		ctx.JSON(http.StatusBadRequest, ErrorResponse(errParams))
		return
	}
	// 查询
	var records []model.ItemEvent
	db := c.db.Model(&model.ItemEvent{})
	if params.IsPagination() {
		db = db.Limit(int(params.PageSize)).Offset(int((params.Current - 1) * params.PageSize))
	}
	//按照作品创建时间查询
	if params.IsBlockTimeSearch() {
		timeLayout := "2006-01-02 15:04:05"  //转化所需模板
		loc, _ := time.LoadLocation("Local") //获取时区
		tmp1, _ := time.ParseInLocation(timeLayout, params.BeginTime, loc)
		beginTimeTrans := tmp1.Unix() //转化为时间戳 类型是int64
		tmp2, _ := time.ParseInLocation(timeLayout, params.EndTime, loc)
		endTimeTrans := tmp2.Unix() //转化为时间戳 类型是int64
		db = db.Where("item_events.block_time between ? and ?", beginTimeTrans, endTimeTrans)
	}

	db = db.Where(&model.ItemEvent{DCI: params.DCI, From: params.From, EventName: params.EventName})
	err = db.Find(&records).Error
	if err != nil {
		log.Logger.Debug("error", zap.Error(err), zap.String("code", errDBError.Error()))
		ctx.JSON(http.StatusInternalServerError, ErrorResponse(errDBError))
		return
	}
	var data []entity.ItemEvent
	for i := 0; i < len(records); i++ {
		data = append(data, entity.ItemEvent{
			TxHash:      records[i].TxHash,
			DCI:         records[i].DCI,
			From:        records[i].From,
			To:          records[i].To,
			EventName:   records[i].EventName,
			BlockNumber: strconv.FormatUint(records[i].BlockNumber, 10),
			BlockTime:   strconv.FormatUint(records[i].BlockTime, 10),
		})
	}
	ctx.JSON(http.StatusOK, SuccessResponse(records))
}

func (c *Controller) generateCertificate(item entity.Item) error {
	wd, _ := os.Getwd()
	im, err := gg.LoadImage(wd + "/resource/certificate.png")
	if err != nil {
		return err
	}
	w := im.Bounds().Size().X
	h := im.Bounds().Size().Y

	dc := gg.NewContext(w, h)
	dc.DrawImage(im, 0, 0)
	dc.SetRGB(0, 0, 0)
	if err := dc.LoadFontFace(wd+"/resource/font.ttf", 50); err != nil {
		return err
	}
	dc.DrawStringAnchored("数字内容存证证明", float64(w/2), 200, 0.5, 0.5)
	if err := dc.LoadFontFace(wd+"/resource/font.ttf", 15); err != nil {
		return err
	}

	label := []string{
		"作品名称:",
		"数字指纹:",
		"存证用户:",
		"存证时间:",
	}
	dc.DrawStringAnchored(label[0], 110, 300, 0.5, 0.5)
	dc.DrawStringAnchored(label[1], 110, 350, 0.5, 0.5)
	dc.DrawStringAnchored(label[2], 110, 430, 0.5, 0.5)
	dc.DrawStringAnchored(label[3], 110, 480, 0.5, 0.5)

	if err := dc.LoadFontFace(wd+"/resource/font.ttf", 15); err != nil {
		return err
	}

	text := []string{
		item.Name,
		item.DCI[:40],
		item.DCI[40:],
		item.CreatorAddress,
		time.Unix(int64(item.BlockTime), 0).Format("2006年01月02日15时04分05秒"),
	}
	//fmt.Println(item.Name,item.DCI,item.CreatorAddress,time.Unix(int64(item.BlockTime), 0).Format("2006年01月02日15时04分05秒"))

	dc.DrawStringWrapped(text[0], 250, 300, 0.5, 0.5, 200, 1.5, gg.AlignLeft)
	dc.DrawStringWrapped(text[1], 250, 350, 0.5, 0.5, 200, 1.5, gg.AlignLeft)
	dc.DrawStringWrapped(text[2], 250, 380, 0.5, 0.5, 200, 1.5, gg.AlignLeft)
	dc.DrawStringWrapped(text[3], 250, 430, 0.5, 0.5, 200, 1.5, gg.AlignLeft)
	dc.DrawStringWrapped(text[4], 250, 480, 0.5, 0.5, 200, 1.5, gg.AlignLeft)

	filePath := fmt.Sprintf("%s.png", filepath.Join(c.config.Local.CertificateDir, item.DCI))
	err = dc.SavePNG(filePath)
	if err != nil {
		return err
	}
	return nil
}

func (c *Controller) generateCertificatePDF(item entity.Item) error {
	pdf := gopdf.GoPdf{}
	pdf.Start(gopdf.Config{PageSize: gopdf.Rect{W: 338, H: 505}})
	pdf.AddPage()
	err := pdf.AddTTFFont("loma", path.Join(c.config.Local.ResourceDir, "font.ttf"))
	if err != nil {
		return err
	}
	err = pdf.Image(path.Join(c.config.Local.ResourceDir, "certificate.png"), 0, 0, nil)
	if err != nil {
		return err
	}
	err = pdf.SetFont("loma", "", 10)
	if err != nil {
		return err
	}
	label := []string{
		"作品名称:",
		"数字指纹:",
		"存证用户地址:",
		"存证用户:",
		"存证时间:",
		"当前拥有者地址:",
		"当前拥有者:",
		"交易时间:",
	}
	pdf.SetX(30)
	pdf.SetY(120)
	err = pdf.Text(label[0])
	if err != nil {
		return err
	}
	pdf.SetX(30)
	pdf.SetY(150)
	err = pdf.Text(label[1])
	if err != nil {
		return err
	}
	pdf.SetX(30)
	pdf.SetY(180)
	err = pdf.Text(label[2])
	if err != nil {
		return err
	}
	pdf.SetX(30)
	pdf.SetY(210)
	err = pdf.Text(label[3])
	if err != nil {
		return err
	}
	pdf.SetX(30)
	pdf.SetY(240)
	err = pdf.Text(label[4])
	if err != nil {
		return err
	}
	pdf.SetX(30)
	pdf.SetY(270)
	err = pdf.Text(label[5])
	if err != nil {
		return err
	}
	pdf.SetX(30)
	pdf.SetY(300)
	err = pdf.Text(label[6])
	if err != nil {
		return err
	}
	pdf.SetX(30)
	pdf.SetY(325)
	err = pdf.Text(label[7])
	if err != nil {
		return err
	}

	text := []string{
		item.Name,
		item.DCI[:40],
		item.DCI[40:],
		item.CreatorAddress[:35],
		item.CreatorAddress[35:],
		item.CreatorUsername,
		time.Unix(int64(item.BlockTime), 0).Format("2006年01月02日15时09分05秒"),
		item.OwnerAddress[:34],
		item.OwnerAddress[34:],
		item.OwnerUsername,
		time.Unix(int64(item.BlockTime), 0).Format("2006年01月02日15时04分05秒"),
	}

	pdf.SetX(80)
	pdf.SetY(120)
	err = pdf.Text(text[0])
	if err != nil {
		return err
	}

	pdf.SetX(80)
	pdf.SetY(150)
	err = pdf.Text(text[1])
	if err != nil {
		return err
	}

	pdf.SetX(80)
	pdf.SetY(160)
	err = pdf.Text(text[2])
	if err != nil {
		return err
	}

	pdf.SetX(100)
	pdf.SetY(180)
	err = pdf.Text(text[3])
	if err != nil {
		return err
	}
	pdf.SetX(100)
	pdf.SetY(190)
	err = pdf.Text(text[4])
	if err != nil {
		return err
	}
	pdf.SetX(80)
	pdf.SetY(210)
	err = pdf.Text(text[5])
	if err != nil {
		return err
	}

	pdf.SetX(80)
	pdf.SetY(240)
	err = pdf.Text(text[6])
	if err != nil {
		return err
	}

	pdf.SetX(106)
	pdf.SetY(270)
	err = pdf.Text(text[7])
	if err != nil {
		return err
	}
	pdf.SetX(106)
	pdf.SetY(280)
	err = pdf.Text(text[8])
	if err != nil {
		return err
	}
	pdf.SetX(90)
	pdf.SetY(300)
	err = pdf.Text(text[9])
	if err != nil {
		return err
	}

	pdf.SetX(80)
	pdf.SetY(325)
	err = pdf.Text(text[10])
	if err != nil {
		return err
	}

	filePath := fmt.Sprintf("%s.pdf", filepath.Join(c.config.Local.CertificateDir, item.DCI))
	err = pdf.WritePdf(filePath)
	if err != nil {
		return err
	}
	return nil
}

func (c *Controller) GetItemPictures(ctx *gin.Context) {
	var param entity.GetItemPicturesUri
	err := ctx.BindUri(&param)
	if err != nil {
		log.Logger.Debug("error", zap.Error(err), zap.String("code", errParams.Error()))
		ctx.JSON(http.StatusBadRequest, ErrorResponse(errParams))
		return
	}
	// 获取作品数据
	var itemRecord model.Item
	err = c.db.Preload("Creator").Preload("Owner").Preload("Create").Preload("Transfer").Where(model.Item{DCI: param.DCI}).First(&itemRecord).Error
	if err != nil {
		log.Logger.Debug("error", zap.Error(err), zap.String("code", errItemNotExist.Error()))
		ctx.JSON(http.StatusBadRequest, ErrorResponse(errItemNotExist))
		return
	}
	var itemEventRecord model.ItemEvent
	err = c.db.Where(model.ItemEvent{DCI: param.DCI, EventName: "create"}).First(&itemEventRecord).Error
	if err != nil {
		ctx.JSON(http.StatusBadRequest, ErrorResponse(errItemNotExist))
		return
	}
	//转换
	data := entity.Item{
		DCI: itemRecord.DCI,
	}
	filePath := fmt.Sprintf("%s", filepath.Join(c.config.Local.FileDir, data.DCI))
	ctx.File(filePath)

}
